/**
 * @(#)ClusterCount.java      1.00 03/09/08
 *
 * Copyright (c) 2000-2003 IBTechnology, Inc.
 * All rights reserved.
 * 
 * This software is the confidential and proprietary information of
 * IBTechnology, Inc. ("Confidential Information"). You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with IBTechnology.
 */
package libnetdb.dm.assoc.util;

import java.util.*;

/**
 * <code>ClusterCount</code> counts the number of records satisfying the cluster 
 * criteria. The argument of <code>count()</code> specifies the cluster 
 * criteria.
 * <p>
 * Example code:
 * <p><blockquote><pre>
 *     ArrayList ar = new ArrayList();
 *     ar.add(new String[]{"�k","�j��","�x�_��","10"});
 *     ar.add(new String[]{"�k","����","�x�_��","55"});
 *     ar.add(new String[]{"�k","����","�x�_��","10"});
 *     ar.add(new String[]{"�k","�ꤤ","�x�n��","55"});
 *     ar.add(new String[]{"�k","�j��","�x�_��","10"});
 *     ar.add(new String[]{"�k","�j��","�x�_��","55"});
 *     ar.add(new String[]{"�k","����","�x�_��","55"});
 *     ar.add(new String[]{"�k","����","�x�_��","10"});
 *     ar.add(new String[]{"�k","����","�x�_��","10"});
 *     ar.add(new String[]{"�k","����","�x�n��","55"});
 *	   ar.add(new String[]{"�k","�ꤤ","�x�_��","10"});
 *	   ar.add(new String[]{"�k","�ꤤ","�x�n��","55"});
 *	   ar.add(new String[]{"�k","�ꤤ","�x�n��","55"});
 *	   
 *	   ClusterCount cc = new ClusterCount(ar);
 *     System.out.println(cc.count(new String[]{"�k"})); //4
 *     System.out.println(cc.count(new String[]{"�k","����"})); //2
 *     System.out.println(cc.count(new String[]{"�k","����","�x�_��"})); //3
 *     System.out.println(cc.count(new String[]{"�k","����","�x�_��","10"})); //2
 *     String[] value = cc.attValues(3);
 *     for (int i = 0; i < value.length; i++) {
 *         System.out.println(value[i]); //10 55 (in two lines)
 *     }
 *     System.out.println(cc.nextAttNonzeroValueCount(new String[]{"�k","�j��"})); //1�A�]���ŦX�k�B�j�Ǫ��u����b�x�_���o�@�ء]�S���x�n���^
 * 
 *     System.out.println(cc.rowspanCount(new String[]{"�k"},0));//1
 *	   System.out.println(cc.rowspanCount(new String[]{"�k"},1));//3�A(�j�ǡB�����B�ꤤ)�@�T��
 *	   System.out.println(cc.rowspanCount(new String[]{"�k"},2));//5�A(�j�ǥB�x�_���B�����B�x�_���B�����B�x�n���B�ꤤ�B�x�_���B�ꤤ�B�x�n��)�@����
 *	   System.out.println(cc.rowspanCount(new String[]{"�k"},3));//7�A(�j�ǥB�x�_���B10�B�j�ǥB�x�_���B55�B�����B�x�_���B10�B�����B�x���B55�B�����B�x�n���B55�B�ꤤ�B�x�_���B10�B�ꤤ�B�x�n���B55)�@�C��
 *	   System.out.println(cc.rowspanCount(new String[]{"�k","����"},3));//3�A(�x�_���B55�B�x�_���B10�B�x�n���B55)�@�T��		
 * </pre></blockquote>
 * <p>
 * @see
 * @version 1.00   09/08/03
 * @author  Pin Ting
 */
public class ClusterCount {
	/**
	 * @param record an ArrayList of which elements are String arrays
	 */
	public ClusterCount(List record) {
		recordCount = record.size();
		if (recordCount > 0 ) {
			attributeCount = ((String[])record.get(1)).length;
		}
		records = new String[recordCount][attributeCount];
		for (int i = 0; i < recordCount; i++) {
			String[] cr = (String[])record.get(i); //current record
			try {			
			    if (attributeCount != cr.length) {
				    throw new IllegalArgumentException("The numbers of attributes among records are inconsistent!");
			    }
			}
			catch (IllegalArgumentException e) {
				e.printStackTrace();
			}
			for (int j = 0; j < cr.length; j++) {
				records[i][j] = new String(cr[j]);	
			}			
		}
	}
    private String[][] records;
    private int recordCount;
    private int attributeCount;
    
    /**
     * Count the number of records matching the specified cluster criteria.
     * @param attributes the cluster criteria.
     * @return the number of records matching the specifid cluster criteria.
     */
    public int count(String[] attributes) {
    	
    	if (attributes.length > attributeCount ||
    	    attributes.length == 0) {
    		return 0;
    	}
		
		int ret = recordCount;
    	int[] matchRecords = new int[recordCount];
    	
    	for (int i = 0; i < matchRecords.length; i++) {
    		matchRecords[i] = i; //initially, all records are candidates
    	}
    	
    	int currentMatch = 0;
    	
    	for (int i = 0; i < attributes.length; i++) { //filtered with attributes one by one  
    		for (int j = 0; j < ret; j++) { //go thru the remained records
    		    if (records[matchRecords[j]][i].equals(attributes[i])) {
    		    	matchRecords[currentMatch++] = matchRecords[j]; //keep matched records
    		    }    		
    		}
    		ret = currentMatch; //# of records passing filter till this attribute
    		currentMatch =0;
    	}    	
    	
    	return ret;    	 
    }
    
    
    
    public int attSize() {
    	return attributeCount;
    }
    /**
     * @param attributIndex index to attribute of which values are 
     *        queried against (attributeIndex is 0-based).
     * @return all possible values of the indicated attribute.
     */
    public String[] attValues(int attributeIndex) {
    	
    	try {
    		if (attributeIndex + 1 > attributeCount) {
    	    	throw new IllegalArgumentException(
					"attributeIndex exceeds the number of attributes in each record!");
    	    }
    	}
    	catch (IllegalArgumentException e) {
    		e.printStackTrace();
    	}
    	
    	Set valueSet = new TreeSet();
    	
    	for (int i = 0; i < recordCount; i++) {
    		valueSet.add(records[i][attributeIndex]);
    	}
    	
    	String[] ret = (String[])valueSet.toArray(new String[valueSet.size()]);
    	return ret;    	    	
    }
    
    /**
     * Count the number of nonzero-count values of the attribute
     * one level down to those specified in attributes argument.
     * @param attributes the cluster criteria.
     * @return the number of nonzero-count values of the attribute
     * one level down to those specified in attributes argument.
     */
    public int nextAttNonzeroValueCount(String[] attributes) {
    	int ret = 0;
    	String[] nextAttValues = attValues(attributes.length);
    	String[] attributesTemplate = new String[attributes.length + 1];
    	System.arraycopy(attributes, 0, attributesTemplate, 0, attributes.length);
    	
    	for (int i = 0; i < nextAttValues.length; i++) {
			attributesTemplate[attributes.length] = nextAttValues[i];
			if (count(attributesTemplate) != 0) ret++;
    	}
    	
    	return ret;
    }
    
    /**
     * To facilitate HTML layout work, <code>rowspanCount(String[] 
     * attributes, int attributeIndex)</code> counts the value of rowspan 
     * rooted from <code>attributes</code>, extending to <code>attributeIndex</code>
     * level. The rowspan value counts only the number of nonzero-count 
     * values of the (<code>attributeIndex</code>)th attributes.
     * 
     * @param attributes the cluster criteria.
     * @param attributeIndex index to attribute level in which the number 
     *        of rowspan is interested (attributeIndex is 0-based).
     * @return the number of nonzero-count values of the (<code>attributeIndex</code>)th 
     *         attributes, rooted from <code>attributes</code>.
     */
    public int rowspanCount(String[] attributes, int attributeIndex) {
    	
    	try {
    		if (attributes.length > attributeIndex + 1) {
    			throw new IllegalArgumentException(
					"attributeIndex points into attributes!");
    		}
			else if (attributeIndex + 1 > attributeCount) {
		        throw new IllegalArgumentException(
			        "attributeIndex exceeds the number of attributes in each record!");
	        }
        }
		catch (IllegalArgumentException e) {
			e.printStackTrace();
		}
    	
    	if (attributes.length == attributeIndex + 1) {
    		return 1;
    	} 
    	
    	String[] attValues = this.attValues(attributes.length);
    	String[] attTemplate = new String[attributes.length + 1];
    	System.arraycopy(attributes, 0, attTemplate, 0, attributes.length);
		int ret = 0;
    	
    	for (int i = 0; i < attValues.length; i++) {
    		attTemplate[attTemplate.length - 1] =  attValues[i];
    		if (count(attTemplate) != 0) {
    			ret += rowspanCount(attTemplate, attributeIndex); 
    	    }
        }    	
    	
    	return ret;  
    }
}

